home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 05 - 1989 / 05.02 Feb 89 / security code / SecurityPatrol.Pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1988-11-29  |  11.7 KB  |  489 lines  |  [TEXT/MPS ]

  1. PROGRAM  SecurityPatrol(OUTPUT);
  2. {-------------------------------------------}
  3. (*
  4. ©1988 by Steve Seaquist. All rights reserved.
  5. Used by permission.  Use at your own risk.  
  6. No warranty is expressed or implied.  
  7.  
  8. This Macintosh virus-detecting program was 
  9. originally published and explained in the 
  10. February 1989 issue of MacTutor magazine.  
  11. Some aspects of its design are important to 
  12. security, and it uses some unusual 
  13. techniques, so please read the article.  
  14. *)
  15. {-------------------------------------------}
  16. USES
  17.   MacIntf,BitProcs,CodeSizeLimits,Globals,
  18.   MainDlog,PasLibIntf,Patrol;
  19. {$R-}
  20.  
  21. CONST
  22.   kPreprocessSelf     = TRUE;
  23.   kAwaitVerification  = FALSE;
  24.  
  25. VAR
  26.   gInitSecPatDone:    BOOLEAN;
  27.   gRptFileOpen:       BOOLEAN;
  28.   gScrDmpEnbPtr:      Ptr;
  29.   gScrDmpEnbSave:     SignedByte;
  30.   o:                  Text;
  31.  
  32. PROCEDURE  PreprocessSelf;           FORWARD;
  33. PROCEDURE  Wryte
  34.   (pStr:     Str255);                FORWARD;
  35. PROCEDURE  WryteChar
  36.   (pChar:    CHAR);                  FORWARD;
  37. PROCEDURE  WryteEoln;                FORWARD;
  38. PROCEDURE  WryteLn
  39.   (pStr:     Str255);                FORWARD;
  40. PROCEDURE  WryteNbr
  41.   (pNbr:     LONGINT;
  42.   pNbrDigits:INTEGER);               FORWARD;
  43. PROCEDURE  WryteType
  44.   (pType:    ResType);               FORWARD;
  45.  
  46. {-------------------------------------------}
  47. {$Z*}
  48. {-------------------------------------------}
  49. PROCEDURE  ExitSecurityPatrol;
  50. BEGIN
  51. IF  gOption[eTrace] THEN
  52.   Trace('ExitSecurityPatrol');
  53. gScrDmpEnbPtr^ := gScrDmpEnbSave;
  54. IF  gRptFileOpen THEN
  55.   Close(o);
  56. ExitToShell;
  57. END;
  58. {-------------------------------------------}
  59. {$Z-}
  60. {-------------------------------------------}
  61. PROCEDURE  InitSecurityPatrol;
  62. VAR
  63.   sConfig:   LONGINT;
  64. BEGIN
  65. MaxApplZone;
  66. MoreMasters; MoreMasters; MoreMasters;
  67. gInitSecPatDone := FALSE;
  68. gRptFileOpen    := FALSE;
  69. gScrDmpEnbPtr   := Ptr(kScrDmpEnb);
  70. gScrDmpEnbSave  := gScrDmpEnbPtr^;
  71. gScrDmpEnbPtr^  := 0;
  72. Textbook(@thePort);
  73. Write  ('SecurityPatrol is a Mac virus ');
  74. Write  ('detector.  ');
  75. TextFace([bold,extend]);
  76. WriteLn('Use at your own risk.  ');
  77. TextFace([]);
  78. Write  ('The Save As... dialog below is ');
  79. WriteLn('to save error reports.');
  80. PLFlush(OUTPUT);
  81. PauseBriefly;
  82.  
  83. InitGlobals;
  84. gOption[eBeeps]   := TRUE; {override…}
  85. gDisabled[eFgPrC] := TRUE; {…defaults}
  86. InitMainDlog;
  87. InitPatrols;
  88.  
  89. sConfig := BAnd(ORD4(Ptr(kSPConfig)^),$F);
  90. IF  (sConfig = useFree)
  91. OR  (sConfig = useAsync) THEN
  92.   SFPutFile
  93.     (gSFPutPt,'Filename, or cancel to print',
  94.     'SecurityPatrol Report',NIL,gSFRep)
  95. ELSE
  96.   SFPutFile
  97.     (gSFPutPt,'Filename, or cancel to quit',
  98.     'SecurityPatrol Report',NIL,gSFRep);
  99. WITH gSFRep DO
  100.   IF good THEN
  101.     BEGIN
  102.     gCurrWDRefNum := vRefNum;
  103.     BuildDirname;
  104.     ReWrite(o,CONCAT(gCurrDirname,fName));
  105.     END
  106.   ELSE
  107.     IF  (sConfig = useFree)
  108.     OR  (sConfig = useAsync) THEN
  109.       ReWrite(o,'PRINTER:')
  110.     ELSE
  111.       BEGIN
  112.       WriteLn('Run cancelled.');
  113.       PLFlush(OUTPUT);
  114.       PauseBriefly;
  115.       ExitSecurityPatrol;
  116.       END;
  117. gRptFileOpen := TRUE;
  118.  
  119. Wryte  ('This copy of Security Patrol 1.0 ');
  120. Wryte  ('is being maintained by ');
  121. TextFace([bold,extend]);
  122. gPgmrname := '<<your name here>>';
  123. WryteLn(gPgmrname);
  124. TextFace([]);
  125. Wryte  ('The following run was done on ');
  126. GetTime(gDateTimeRec);
  127. WITH gDateTimeRec DO
  128.   BEGIN
  129.   year := year mod 100;
  130.   WryteNbr (month,2);
  131.   WryteChar('/');
  132.   WryteNbr (day,  2);
  133.   WryteChar('/');
  134.   WryteNbr (year, 2);
  135.   Wryte    (' at ');
  136.   WryteNbr (hour, 2);
  137.   WryteChar(':');
  138.   IF  minute < 10 THEN
  139.     WryteChar('0');
  140.   WryteNbr (minute,1);
  141.   WryteLn  ('.');
  142.   END;
  143. IF  kPreprocessSelf THEN
  144.   PreprocessSelf
  145. ELSE
  146.   WryteLn('Didn’t perform self-tests.');
  147. gInitSecPatDone := TRUE;
  148. END;
  149. {-------------------------------------------}
  150. PROCEDURE  PreprocessSelf;
  151. VAR
  152.   i:          INTEGER;
  153.   sC1Ptr:     LONGINT;
  154.   sEntActual: LONGINT;
  155.   sEntShouldB:LONGINT;
  156.   sOffActual: LONGINT;
  157.   sOffShouldB:LONGINT;
  158.   sResType:   ResType;
  159.   sSave:      TMainOpt;
  160.   {--------------------}
  161.   PROCEDURE  Abort
  162.     (pStr:     Str255);
  163.   BEGIN
  164.   ErrorBegins(pStr);
  165.   WryteEoln;
  166.   CommentBegins;
  167.   WryteLn('Assuming infected.');
  168.   CommentBegins;
  169.   Wryte  ('Contact ');
  170.   Wryte  (gPgmrname);
  171.   WryteLn(' to be sure.');
  172.   CommentBegins;
  173.   Wryte  ('(These msgs apply to ');
  174.   Wryte  (gCurrFilename);
  175.   WryteLn(' itself, not to your system.)');
  176.   CommentBegins;
  177.   gOption[eAwait] := TRUE;
  178.   gOption[eBeeps] := TRUE;
  179.   ErrorEnds(4);
  180.   ExitSecurityPatrol;
  181.   END;
  182.   {--------------------}
  183. BEGIN
  184. BlockMove(@gOption,@sSave,SIZEOF(TMainOpt));
  185. ZeroOut  (@gOption,       SIZEOF(TMainOpt));
  186. gOption[eRmVir] := TRUE;
  187. { gOption[eTrace] := TRUE; }
  188. IF  gOption[eTrace] THEN
  189.   Trace('PreprocessSelf');
  190. GetCodeSizeLimits;
  191. GetRsrc(@gCode0,'CODE',0,ResId);
  192. IF  (gCode0.fFlag <> kRsrcHdlValid) THEN
  193.   Abort('Unable to get own CODE 0');
  194. IF  NOT(Code0IsValid) THEN
  195.   Abort('Found unexpected CODE 0 header');
  196. LookForKnownViruses;
  197. FOR i := 1 TO Count1Types DO
  198.   BEGIN
  199.   Get1IndType(sResType,i);
  200.   IF  (sResType = 'SIZE') THEN
  201.     BEGIN
  202.     IF  (Count1Resources('SIZE') > 1) THEN
  203.       Abort('Too many SIZE resources');
  204.     GetRsrc(@gCurrRsrc,'SIZE',1,Index);
  205.     IF  (gCurrRsrc.fSize > 10) THEN
  206.       Abort('SIZE resource too large');
  207.     ReleaseRsrc(@gCurrRsrc);
  208.     END
  209.   ELSE IF (sResType <> 'CODE') THEN
  210.     BEGIN
  211.     ErrorBegins('Found a rsrc of type ');
  212.     WryteType(sResType);
  213.     ErrorEnds(0);
  214.     Abort    ('Only CODE and SIZE allowed');
  215.     END;
  216.   END;
  217. WITH TJTHdl(gCode0.fHdl)^^,fJTEntry[1] DO
  218.   BEGIN
  219.   IF  (fNbrBytesInTable <> gJTSize) THEN
  220.     BEGIN
  221.     ErrorBegins('Jump table size is ');
  222.     WryteNbr (fNbrBytesInTable,1);
  223.     Wryte    (', should be ');
  224.     WryteNbr (gJTSize,1);
  225.     ErrorEnds(0);
  226.     Abort    ('Invalid Jump Table size');
  227.     END;
  228.   IF  NOT(JTEIsValid(@fJTEntry[1])) THEN
  229.     Abort('Invalid Jump Table entry');
  230.   IF  (fSegId  <> 1) THEN
  231.     BEGIN
  232.     ErrorBegins('Enters at CODE ');
  233.     WryteNbr(fSegId,1);
  234.     Wryte   (', should enter at CODE 1');
  235.     ErrorEnds(0);
  236.     Abort   ('Invalid start address');
  237.     END;
  238.   sOffActual := 4 + fOffset;
  239.   END;
  240. WITH gCurrRsrc DO
  241.   BEGIN
  242.   GetRsrc(@gCurrRsrc,'CODE',1,ResId);
  243.   IF  (fFlag <> kRsrcHdlValid) THEN
  244.     Abort('Couldn’t look at own CODE 1');
  245.   sC1Ptr      := BAND($00FFFFFF,ORD4(fHdl^));
  246.   sEntActual := sC1Ptr + sOffActual;
  247.   IF  (TWordPtr(sEntActual)^ = $4EFA) THEN
  248.     BEGIN
  249.     sOffActual := 
  250.       sOffActual+2+TWordPtr(sEntActual+2)^;
  251.     sEntActual := sC1Ptr + sOffActual;
  252.     END;
  253.   sEntShouldB := gEntryPoint;
  254.   sOffShouldB := sEntShouldB - sC1Ptr;
  255.   IF  (sEntActual <> sEntShouldB) THEN
  256.     BEGIN
  257.     ErrorBegins('Invalid start address');
  258.     WryteEoln;
  259.     CommentBegins;
  260.     Wryte   ('Enters at address $');
  261.     ShortHexDump(@sEntActual,4);
  262.     Wryte   (' (CODE 1 + $');
  263.     ShortHexDump(@sOffActual,4);
  264.     Wryte   (') --> $');
  265.     ShortHexDump(Ptr(sEntActual),4);
  266.     WryteEoln;
  267.     CommentBegins;
  268.     Wryte   ('Should enter at   $');
  269.     ShortHexDump(@sEntShouldB,4);
  270.     Wryte   (' (CODE 1 + $');
  271.     ShortHexDump(@sOffShouldB,4);
  272.     Wryte   (') --> $');
  273.     ShortHexDump(Ptr(sEntShouldB),4);
  274.     WryteEoln;
  275.     CommentBegins;
  276.     Wryte   ('CODE 1 begins at  $');
  277.     ShortHexDump(@sC1Ptr,4);
  278.     ErrorEnds(0);
  279.     Abort   ('This is not a user error');
  280.     END;
  281.   ReleaseRsrc(@gCurrRsrc);
  282.   END;
  283. ReleaseRsrc(@gCode0);
  284.  
  285. IF  (Count1Resources('CODE')>gMaxCode+1) THEN
  286.   Abort('Too many CODE resources.');
  287. IF  kAwaitVerification THEN
  288.   BEGIN
  289.   ErrorBegins('The following are the ');
  290.   Wryte  ('“fingerprints” of ');
  291.   Wryte  (gCurrFilename);
  292.   Wryte  (' itself:');
  293.   ErrorEnds(0);
  294.   END;
  295. FOR i := 0 TO gMaxCode DO
  296.   WITH gCurrRsrc DO
  297.     BEGIN
  298.     GetRsrc(@gCurrRsrc,'CODE',i,ResId);
  299.     IF  (fFlag <> kRsrcHdlValid) THEN
  300.       Abort('Couldn’t look at next CODE');
  301.     IF  (fSize > gSizeLimit[i]) THEN
  302.       BEGIN
  303.       ErrorBegins('Failed a CODE size test');
  304.       WryteEoln;
  305.       CommentRsrcBegins(@gCurrRsrc);
  306.       Wryte   (' size is ');
  307.       WryteNbr(fSize,1);
  308.       Wryte   (', which exceeds its ');
  309.       WryteNbr(gSizeLimit[i],1);
  310.       WryteLn (' size limit.');
  311.       Abort('May contain an imbedded virus');
  312.       END;
  313.     IF  kAwaitVerification THEN
  314.       BEGIN
  315.       ProcessCurrRsrc;
  316.       CommentFgPrRsrc(@gCurrRsrc);
  317.       END;
  318.     ReleaseRsrc(@gCurrRsrc);
  319.     END;
  320. IF  kAwaitVerification THEN
  321.   BEGIN
  322.   ErrorBegins('Time to make a decision:');
  323.   WryteEoln;
  324.   CommentBegins;
  325.   WryteLn('Verify fingerprints if you can');
  326.   CommentBegins;
  327.   WryteLn('Press command-period to abort');
  328.   CommentBegins;
  329.   WryteLn('Press any other key to continue');
  330.   CommentBegins;
  331.   gOption[eAwait] := TRUE;
  332.   ErrorEnds(0);
  333.   IF  gAbortPatrol THEN
  334.     BEGIN
  335.     PauseBriefly;
  336.     ExitSecurityPatrol;
  337.     END;
  338.   END;
  339. WryteLn('Passed all current self-tests.');
  340. PauseBriefly;
  341. BlockMove(@sSave,@gOption,SIZEOF(TMainOpt));
  342. END;
  343. {-------------------------------------------}
  344. {$Z*}
  345. {-------------------------------------------}
  346. PROCEDURE  WriteFilenameToReport;
  347. BEGIN
  348. WITH gReportFlags DO
  349.   IF  NOT(fWroteFilename) THEN
  350.     BEGIN
  351.     IF  NOT(fWroteDirname) THEN
  352.       BEGIN
  353.       WriteLn(o,gCurrDirname);
  354.       fWroteDirname := TRUE;
  355.       END;
  356.     Write(o,gInd,gCurrFilename);
  357.     IF  gActiveSelf THEN
  358.       BEGIN
  359.       Write(o,' (Active Self)');
  360.       IF  gInitSecPatDone
  361.       AND NOT(kProcessSelf) THEN
  362.         Write(o,' skipped');
  363.       END
  364.     ELSE IF gActiveSys THEN
  365.       Write(o,' (Active System)');
  366.     WriteLn(o);
  367.     fWroteFilename := TRUE;
  368.     END;
  369. END;
  370. {-------------------------------------------}
  371. PROCEDURE  WriteFilenameToScreen;
  372. BEGIN
  373. WITH gScreenFlags DO
  374.   IF  NOT(fWroteFilename) THEN
  375.     BEGIN
  376.     IF  NOT(fWroteDirname) THEN
  377.       BEGIN
  378.       WriteLn(gCurrDirname);
  379.       fWroteDirname := TRUE;
  380.       END;
  381.     Write(gInd,gCurrFilename);
  382.     IF  gActiveSelf THEN
  383.       BEGIN
  384.       Write(' (Active Self)');
  385.       IF  gInitSecPatDone
  386.       AND NOT(kProcessSelf) THEN
  387.         Write(' skipped');
  388.       END
  389.     ELSE IF gActiveSys THEN
  390.       Write(' (Active System)');
  391.     WriteLn;
  392.     PLFlush(OUTPUT);
  393.     fWroteFilename := TRUE;
  394.     END;
  395. END;
  396. {-------------------------------------------}
  397. PROCEDURE  Wryte
  398.   (pStr:     Str255);
  399. BEGIN
  400. Write(pStr);
  401. IF  gRptFileOpen THEN
  402.   Write(o,pStr);
  403. END;
  404. {-------------------------------------------}
  405. PROCEDURE  WryteChar
  406.   (pChar:    CHAR);
  407. BEGIN
  408. Write(pChar);
  409. IF  gRptFileOpen THEN
  410.   Write(o,pChar);
  411. END;
  412. {-------------------------------------------}
  413. PROCEDURE  WryteEoln;
  414. BEGIN
  415. WriteLn;
  416. PLFlush(OUTPUT);
  417. IF  gRptFileOpen THEN
  418.   BEGIN
  419.   WriteLn(o);
  420.   { PLFlush(o); control w/option chk box? }
  421.   END;
  422. END;
  423. {-------------------------------------------}
  424. PROCEDURE  WryteFilename;
  425. BEGIN
  426. IF  gRptFileOpen THEN
  427.   WriteFilenameToReport;
  428. WriteFilenameToScreen;
  429. END;
  430. {-------------------------------------------}
  431. PROCEDURE  WryteFilenameToScreenOnlyForNow;
  432. BEGIN
  433. WriteFilenameToScreen;
  434. END;
  435. {-------------------------------------------}
  436. PROCEDURE  WryteLn
  437.   (pStr:     Str255);
  438. BEGIN
  439. WriteLn(pStr);
  440. PLFlush(OUTPUT);
  441. IF  gRptFileOpen THEN
  442.   BEGIN
  443.   WriteLn(o,pStr);
  444.   { PLFlush(o); control w/option chk box? }
  445.   END;
  446. END;
  447. {-------------------------------------------}
  448. PROCEDURE  WryteNbr
  449.   (pNbr:     LONGINT;
  450.   pNbrDigits:INTEGER);
  451. BEGIN
  452. Write(pNbr:pNbrDigits);
  453. IF  gRptFileOpen THEN
  454.   Write(o,pNbr:pNbrDigits);
  455. END;
  456. {-------------------------------------------}
  457. PROCEDURE  WryteType
  458.   (pType:    ResType);
  459. BEGIN
  460. Write(pType);
  461. IF  gRptFileOpen THEN
  462.   Write(o,pType);
  463. END;
  464. {-------------------------------------------}
  465. PROCEDURE  zzSecurityPatrol;
  466. BEGIN
  467. END;
  468. {*******************************************}
  469. BEGIN
  470. InitSecurityPatrol;
  471. WHILE TRUE DO
  472.   BEGIN
  473.   gAbortPatrol := FALSE;
  474.   CASE MainDlogWorkRequested OF
  475.   eDirs:    PatrolDirectories (FALSE);
  476.   eDiry:    PatrolDirectories (TRUE);
  477.   eEvery:   PatrolEverything;
  478.   eFiles:   PatrolFiles;
  479.   OTHERWISE LEAVE;
  480.   END; {CASE}
  481.   END;
  482. WryteEoln;
  483. WryteLn('*******************************');
  484. WryteEoln;
  485. WryteLn ('Totals over all patrols:');
  486. ListCounts(@gTotals);
  487. ExitSecurityPatrol;
  488. {*******************************************}
  489. END.